package com.lb.uts.service.policy;

import com.lb.uts.constants.TaskTypeEnum;
import com.lb.uts.constants.UTSConstants;
import com.lb.uts.entity.ActiveClientInfo;
import com.lb.uts.entity.JobExecuteRecord;
import com.lb.uts.entity.ScheduleJob;
import com.lb.uts.entity.WaitJob;
import com.lb.uts.quartz.ExecuteCallBack;
import com.lb.uts.service.IJobRecordService;
import com.lb.uts.service.IScheduleJobService;
import com.lb.uts.service.manager.DependManager;
import com.lb.uts.util.PolicyUtil;
import com.lb.uts.util.RscHttpAsyncUtils;
import com.lb.uts.util.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.Future;

/**
 * 执行任务的抽象实现
 *
 * @author liangb
 * @version 3.0
 * @date 2019/4/28 14:19
 */
public abstract class AbstractJobExecute implements IJobExecute {

    private static final Logger logger = LoggerFactory.getLogger(AbstractJobExecute.class);

    @Override
    public void executeJob(ScheduleJob job) {
        //是否允许多台执行
        String slave = job.getSlave();
        if ("00".equals(slave)) {
            //单台执行
            ActiveClientInfo activeClientInfo = PolicyUtil.schedulingPolicyMap.get("policy" + (StringUtils.isBlank(job.getPolicyType()) ? "00" : job.getPolicyType())).getSuitActiveClient(job);
            if (activeClientInfo == null) {
                saveJobRecord(job, null);
            } else {
                singleExecute(job, activeClientInfo, null, null);
            }
        } else {
            //多台执行
            List<ActiveClientInfo> activeClientInfos = PolicyUtil.schedulingPolicyMap.get("policy" + job.getPolicyType()).getSuitActiveClients(job);
            if (activeClientInfos.isEmpty()) {
                saveJobRecord(job, null);
            } else {
                int order = 1;
                int num = activeClientInfos.size();
                for (ActiveClientInfo activeClientInfo : activeClientInfos) {
                    singleExecute(job, activeClientInfo, order++, num);
                }
            }
        }
    }

    /**
     * 一条一条执行
     *
     * @param job
     * @param activeClientInfo
     * @param order            执行器顺序，只有{@link ScheduleJob#getSlave()} 是00时有效
     * @param num              总数量，只有{@link ScheduleJob#getSlave()} 是00时有效
     */
    protected void singleExecute(ScheduleJob job, ActiveClientInfo activeClientInfo, Integer order, Integer num) {
        //保存执行记录
        JobExecuteRecord record = saveJobRecord(job, activeClientInfo);
        if (record == null) {
            return;
        }
        //如果是多台执行,抢先注册

        //验证并行数量
        int i = validConcurrentNum(job, record);
        if (i < 1) {
            return;
        }
        //验证任务依赖
        String host = activeClientInfo.getIp();
        i = validDepend(job, record, host);
        if (i < 1) {
            return;
        }
        //开始执行
        try {
            post(job, record.getId(), activeClientInfo.getIp(), order, num);
        } catch (Exception e) {
            logger.error("任务执行出错：job信息" + job, e);
            SpringContextUtil.getBean(IJobRecordService.class).updateFailRecord(job.getSystemName(), host, record.getId(), "远程调用出错" + e.toString());
        }
    }

    /**
     * 验证并行数量
     *
     * @param job    任务
     * @param record 执行记录
     * @return
     */
    protected int validConcurrentNum(ScheduleJob job, JobExecuteRecord record) {
        String jobId = job.getJobId();
        Integer concurrentNum = job.getConcurrentNum();
        if ("01".equals(job.getSlave())) {
            //多台执行，忽略数量控制
            int num = SpringContextUtil.getBean(IScheduleJobService.class).updateIncRunNum(jobId, record.getExeIp(), concurrentNum);
            if (num <= 0) {
                (SpringContextUtil.getBean(IJobRecordService.class)).updateFailRecord(job.getSystemName(), null, record.getId(), "分片任务已被其他调度器执行");
                return -1;
            }
        } else {
            if (concurrentNum > 0) {
                //数量控制
                int num = SpringContextUtil.getBean(IScheduleJobService.class).updateIncRunNum(jobId, null, concurrentNum);
                if (num <= 0) {
                    (SpringContextUtil.getBean(IJobRecordService.class)).updateFailRecord(job.getSystemName(), null, record.getId(), "同时执行数量不得大于" + job.getConcurrentNum());
                    return -1;
                }
            } else {
                //数量＋1 ，仅仅作为数据统计和变更任务信息时使用
                int num = SpringContextUtil.getBean(IScheduleJobService.class).updateRunNum(jobId, null, concurrentNum, 1);
                if (num <= 0) {
                    (SpringContextUtil.getBean(IJobRecordService.class)).updateFailRecord(job.getSystemName(), null, record.getId(), "变更任务信息失败");
                    return -1;
                }
            }
        }
        return 1;
    }

    /**
     * 保存执行记录
     *
     * @param job
     * @param activeClientInfo
     * @return
     */
    protected JobExecuteRecord saveJobRecord(ScheduleJob job, ActiveClientInfo activeClientInfo) {
        //保存执行记录
        String host = activeClientInfo == null ? null : activeClientInfo.getIp();
        //插入调用记录数据
        JobExecuteRecord record = SpringContextUtil.getBean(IJobRecordService.class).insertJobExecuteRecord(job, UTSConstants.REMOTECALL_STATUS_CALL_SUCCESS, host);
        return record;
    }

    /**
     * 查看定时任务的依赖关系
     *
     * @param job
     * @param record
     * @param host
     */
    protected int validDepend(ScheduleJob job, JobExecuteRecord record, String host) {
        //依赖关系判断
        String depend = job.getDepend();
        boolean isWait = false;
        if (StringUtils.isNotBlank(depend)) {
            int index = depend.indexOf('_');
            //小于0 说明数据有问题，跳过
            if (index > 0) {
                String jobGroup = depend.substring(0, index);
                String jobName = depend.substring(index + 1);
                ScheduleJob scheduleJob = SpringContextUtil.getBean(IScheduleJobService.class).selectUniqeJob(jobName, jobGroup);
                if (scheduleJob != null) {
                    if (scheduleJob.getRunNum() > 0) {
                        //有正在运行的任务，等待
                        isWait = true;
                    } else {
                        int dependJobRecordNum = SpringContextUtil.getBean(IJobRecordService.class).countJobRecordNum(scheduleJob.getJobId(), "04");
                        if (dependJobRecordNum <= 0) {
                            //等待
                            isWait = true;
                        }
                    }
                    //如果等待，修改记录状态
                    if (isWait) {
                        WaitJob waitJob = new WaitJob();
                        waitJob.setJobGroup(jobGroup);
                        waitJob.setJobName(jobName);
                        waitJob.setJobId(scheduleJob.getJobId());
                        waitJob.setRecordId(record.getId());
                        waitJob.setSystemName(job.getSystemName());
                        waitJob.setHost(host);
                        DependManager.addWaitJob(waitJob);
                        record.setResponseContent("等待前置任务:" + depend + ".....");
                        SpringContextUtil.getBean(IJobRecordService.class).update(record);
                        return -1;
                    }
                }
            }
        }
        return 1;
    }


    public static void post(ScheduleJob job, String jobRecordId, String host, Integer order, Integer num) {
        if (host == null) {
            (SpringContextUtil.getBean(IJobRecordService.class)).updateFailRecord(job.getSystemName(), host, jobRecordId, "没有合适的执行器");
            return;
        }
        String url = toUrl(job, host, order, num);
        try {
            Future<HttpResponse> future = RscHttpAsyncUtils.get(url, UTSConstants.TIME_OUT, new ExecuteCallBack(job.getSystemName(), host, jobRecordId));
        } catch (Exception e) {
            logger.error("开始远程调用，地址：" + url);
            throw e;
        }
    }

    private static String toUrl(ScheduleJob job, String host, Integer order, Integer num) {
        StringBuilder sb = new StringBuilder().append("http://");
        if (StringUtils.isNotBlank(host)) { //host
            sb.append(host).append("/");
        }
        sb.append("uts").append("/");
        if (StringUtils.isNotBlank(job.getService())) {
            sb.append(job.getService()).append("/");
        }
        if (StringUtils.isNotBlank(job.getMethod())) {
            sb.append(job.getMethod());//.append(".htm");
        }
        //判断是否有参数
        if (StringUtils.isNotBlank(job.getParameters())) {
            sb.append("?" + job.getParameters());
        }
        //添加特殊参数
        else if (job.getTaskType() != TaskTypeEnum.CODE.getType()) {
            sb.append("?" + UTSConstants.SCRIPT_TYPE + job.getTaskType() + "&" + UTSConstants.SCRIPT_PATH + job.getScriptPath());
        }
        if (order != null && order > 0)
            sb = sb.append("batchPageNo=" + order + "&batchPageTotal=" + num);
        return sb.toString();
    }
}
